/*
Copyright (C) BABEC. All rights reserved.
Copyright (C) THL A29 Limited, a Tencent company. All rights reserved.

SPDX-License-Identifier: Apache-2.0
*/

package main

import (
	"encoding/json"
	"fmt"
	"log"
	"strconv"
	"strings"

	"chainmaker.org/chainmaker/common/v2/crypto"
	"chainmaker.org/chainmaker/contract-sdk-go/v2/pb/protogo"
	"chainmaker.org/chainmaker/contract-sdk-go/v2/sandbox"
	"chainmaker.org/chainmaker/contract-sdk-go/v2/sdk"
)

type EvidenceContract struct {
}

// Page find by page
type Page struct {
	PageSize int
	PageNum  int
}

type EvidenceData struct {
	EvdcHash     string
	Name         string
	CreateTM     uint64
	EvidenceType string
	Remark       string
	TxHash       string
}

type SetEvidenceDataReq struct {
	DataInfo *EvidenceData
	EvdcHash string
}

// InitContract install contract func
func (f *EvidenceContract) InitContract() protogo.Response {
	origin, _ := sdk.Instance.Origin()
	err := sdk.Instance.PutState("ownerinfo", "ownerkey", origin)
	if err != nil {
		return sdk.Error("ERR--InitContract fail")
	}
	return sdk.Success([]byte("Init contract success"))
}

// UpgradeContract upgrade contract func
func (f *EvidenceContract) UpgradeContract() protogo.Response {
	return sdk.Success([]byte("Upgrade contract success"))
}

// InvokeContract the entry func of invoke contract func
func (f *EvidenceContract) InvokeContract(method string) protogo.Response {
	switch method {
	case "transferOwner":
		params := sdk.Instance.GetArgs()
		account := string(params["account"])
		return f.transferOwner(account)
	case "setEvidence":
		return f.setEvidence(Converter1(&SetEvidenceDataReq{}).(*SetEvidenceDataReq))
	case "getEvidence":
		params := sdk.Instance.GetArgs()
		evdcHash := string(params["evdcHash"])
		return f.getEvidence(evdcHash)
	case "getEvidences":
		return f.getEvidences(Converter1(&Page{}).(*Page))
	case "checkEvidenceExist":
		params := sdk.Instance.GetArgs()
		evdcHash := string(params["evdcHash"])
		return f.checkEvidenceExist(evdcHash)
	case "getEvidenceSize":
		return f.getEvidenceSize()
	default:
		return sdk.Error("ERR--invalid method" + method)
	}
}

const (
	mapNameEvidence   = "mapNameEvidence"
	mapNameEvidenceId = "mapNameEvidenceId"
	evidencekeyMaxIdx = "evidencekeyMaxIdx"
)

func (f *EvidenceContract) transferOwner(account string) protogo.Response {

	if len(account) == 0 {
		return sdk.Error("ERR--param error")
	}

	//拥有人
	owner, err := sdk.Instance.GetState("ownerinfo", "ownerkey")
	if err != nil {
		return sdk.Error("ERR--fail to get ownerinfo")
	}

	sender, _ := sdk.Instance.Sender()

	//只有创建人和拥有人才能转移
	if sender == owner {
		err = sdk.Instance.PutState("ownerinfo", "ownerkey", account)
	} else {
		return sdk.Error("ERR--only owner call")
	}

	// 返回结果
	return sdk.Success([]byte("owner changed"))
}

func (f *EvidenceContract) setEvidence(req *SetEvidenceDataReq) protogo.Response {

	// 查询授权信息
	sender, _ := sdk.Instance.Sender()
	owner, err := sdk.Instance.GetState("ownerinfo", "ownerkey")
	if err != nil {
		return sdk.Error("ERR--fail to get ownerinfo")
	}

	if sender != owner {
		return sdk.Error("ERR--only owner ")
	}

	idEvidenceMap, err := sdk.NewStoreMap(mapNameEvidenceId, 1, crypto.HASH_TYPE_SHA256)
	if err != nil {
		return sdk.Error(fmt.Sprintf("ERR--new storeMap of %s failed, err:%s", mapNameEvidenceId, err))
	}

	evidenceMap, err := sdk.NewStoreMap(mapNameEvidence, 1, crypto.HASH_TYPE_SHA256)
	if err != nil {
		return sdk.Error(fmt.Sprintf("ERR--new storeMap of %s failed, err:%s", mapNameEvidence, err))
	}

	evdcHash1 := req.EvdcHash
	if !strings.HasPrefix(evdcHash1, "0x") {
		evdcHash1 = "0x" + evdcHash1
	}
	evdcHash1 = strings.ToUpper(evdcHash1)

	data, _ := evidenceMap.Get([]string{evdcHash1})

	if data != nil && len(data) > 0 {
		return sdk.Error("ERR--evdcHash already exists " + fmt.Sprintf("my data is: %x ，%s", data, evdcHash1))
	}

	// 将ShareItem对象存储在map中
	statehash, _ := sdk.Instance.GetTxId()
	req.DataInfo.TxHash = statehash

	evidencedata, _ := json.Marshal(req.DataInfo)
	errSet := evidenceMap.Set([]string{evdcHash1}, evidencedata)
	if errSet != nil {
		return sdk.Error(fmt.Sprintf("ERR--%s", errSet))
	}

	// 设置 下标，以便分页查询直接先查询下标，再根据id查询（当然也可以直接使用切片存储）
	maxIdxStr, _ := sdk.Instance.GetStateFromKey(evidencekeyMaxIdx)
	nextIdx, _ := strconv.Atoi(maxIdxStr)
	nextIdx++
	sdk.Instance.PutStateFromKey(evidencekeyMaxIdx, strconv.Itoa(nextIdx))
	idEvidenceMap.Set([]string{"idx" + strconv.Itoa(nextIdx)}, []byte(evdcHash1))

	var e []string
	senderOrgId, _ := sdk.Instance.GetSenderOrgId()
	e = append(e, "setEvidence")
	e = append(e, senderOrgId)
	e = append(e, string(evidencedata))

	sdk.Instance.EmitEvent("setevidencedata", e)

	return sdk.Success([]byte("OK"))
}

func (f *EvidenceContract) getEvidence(evdcHash string) protogo.Response {

	evidenceMap, err := sdk.NewStoreMap(mapNameEvidence, 1, crypto.HASH_TYPE_SHA256)
	if err != nil {
		return sdk.Error(fmt.Sprintf("ERR--new storeMap of %s failed, err:%s", mapNameEvidence, err))
	}

	evdcHash1 := evdcHash
	if !strings.HasPrefix(evdcHash, "0x") {
		evdcHash1 = "0x" + evdcHash
	}
	evdcHash1 = strings.ToUpper(evdcHash1)

	data, err2 := evidenceMap.Get([]string{evdcHash1})
	if err2 != nil {
		return sdk.Error(fmt.Sprintf("ERR--%s", err2))
	}

	// 返回结果
	return sdk.Success(data)
}

func (f *EvidenceContract) checkEvidenceExist(evdcHash string) protogo.Response {

	evidenceMap, err := sdk.NewStoreMap(mapNameEvidence, 1, crypto.HASH_TYPE_SHA256)
	if err != nil {
		return sdk.Error(fmt.Sprintf("ERR--new storeMap of %s failed, err:%s", mapNameEvidence, err))
	}

	evdcHash1 := evdcHash
	if !strings.HasPrefix(evdcHash, "0x") {
		evdcHash1 = "0x" + evdcHash
	}
	evdcHash1 = strings.ToUpper(evdcHash1)

	data, _ := evidenceMap.Get([]string{evdcHash1})

	if (data == nil) || (len(data) == 0) {
		return sdk.Success([]byte("false"))
	} else {
		// 返回结果
		return sdk.Success([]byte("true"))
	}
}

func (f *EvidenceContract) getEvidenceSize() protogo.Response {

	maxIdxStr, _ := sdk.Instance.GetStateFromKey(evidencekeyMaxIdx)
	// 返回结果
	return sdk.Success([]byte(maxIdxStr))

}

func (f *EvidenceContract) getEvidences(page *Page) protogo.Response {
	idEvidenceMap, err := sdk.NewStoreMap(mapNameEvidenceId, 1, crypto.HASH_TYPE_SHA256)
	if err != nil {
		return sdk.Error(fmt.Sprintf("ERR--new storeMap of %s failed, err:%s", mapNameEvidenceId, err))
	}
	evidenceMap, err := sdk.NewStoreMap(mapNameEvidence, 1, crypto.HASH_TYPE_SHA256)
	if err != nil {
		return sdk.Error(fmt.Sprintf("ERR--new storeMap of %s failed, err:%s", mapNameEvidence, err))
	}
	if page.PageSize <= 0 {
		page.PageSize = 10
	}
	if page.PageNum <= 0 {
		page.PageNum = 1
	}

	maxIdxStr, _ := sdk.Instance.GetStateFromKey(evidencekeyMaxIdx)
	maxId, _ := strconv.Atoi(maxIdxStr)

	start := (page.PageNum-1)*page.PageSize + 1
	end := start + page.PageSize

	if start > maxId {
		return sdk.Error("ERR--start Index out of bounds")
	}

	if end > maxId {
		end = maxId + 1
	}

	if start == end {
		end++
	}

	evidenceDatas := make([]*EvidenceData, 0)
	for i := start; i < end; i++ {
		evdcHash, err := idEvidenceMap.Get([]string{"idx" + strconv.Itoa(i)})
		if err != nil {
			break
		}
		data, _ := evidenceMap.Get([]string{string(evdcHash)})
		// do something
		evidenceData := &EvidenceData{}
		err = json.Unmarshal(data, evidenceData)
		if err != nil {
			return sdk.Error("ERR--" + err.Error() + " i= " + strconv.Itoa(i) + " evdcHash= " + string(evdcHash) + " data= " + string(data))
		}
		evidenceDatas = append(evidenceDatas, evidenceData)
	}
	resp, _ := json.Marshal(evidenceDatas)
	return sdk.Success(resp)
}

func Converter1(v interface{}) interface{} {
	params := sdk.Instance.GetArgs()

	// 获取参数
	data := string(params["data"])

	err := json.Unmarshal([]byte(data), v)
	if err != nil {
		sdk.Instance.Warnf("WARN--" + err.Error())
	}
	return v
}

func Converter(v interface{}) {
	params := sdk.Instance.GetArgs()

	// 获取参数
	data := string(params["data"])

	err := json.Unmarshal([]byte(data), v)
	if err != nil {
		sdk.Instance.Warnf("WARN--" + err.Error())
	}
}

func main() {
	err := sandbox.Start(new(EvidenceContract))
	if err != nil {
		log.Fatal(err)
	}
}
